package com.pro01.apirednote.controller;

import com.pro01.apirednote.dto.ContentWithFilesResponse;
import com.pro01.apirednote.mapper.ContentFileMapper;
import com.pro01.apirednote.pojo.Content;
import com.pro01.apirednote.pojo.ContentFile;
import com.pro01.apirednote.pojo.PageResult;
import com.pro01.apirednote.pojo.Result;
import com.pro01.apirednote.service.CacheService;
import com.pro01.apirednote.service.ChineseVectorService;
import com.pro01.apirednote.service.ElasticsearchService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import jakarta.servlet.http.HttpServletRequest;

@Slf4j
@RestController
@RequestMapping("/api/search")
@RequiredArgsConstructor
public class SemanticSearchController {

    private final ElasticsearchService elasticsearchService;
    private final ChineseVectorService chineseVectorService;
    private final CacheService cacheService;
    private final RedisTemplate<String, Object> redisTemplate;
    private final ContentFileMapper contentFileMapper;
    
    private static final String SEARCH_CACHE_PREFIX = "search:";
    private static final String SEMANTIC_CACHE_PREFIX = SEARCH_CACHE_PREFIX + "semantic:";
    
    /**
     * 语义检索API
     * @param query 查询文本
     * @param pageNum 页码
     * @param pageSize 每页大小
     * @param minScore 最小相似度分数（0-1之间）
     * @return 检索结果
     */
    @GetMapping("/semantic")
    public Result<PageResult<ContentWithFilesResponse>> semanticSearch(
            @RequestParam String query,
            @RequestParam(defaultValue = "1") int pageNum,
            @RequestParam(defaultValue = "10") int pageSize,
            @RequestParam(defaultValue = "0.5") double minScore) {
        
        log.info("执行语义检索，查询：{}，页码：{}，每页大小：{}，最小分数：{}", query, pageNum, pageSize, minScore);
        Result<PageResult<Content>> esResult = elasticsearchService.semanticSearchContent(query, pageNum, pageSize, minScore);
        
        if (esResult.getCode() != 200) {
            return Result.error(esResult.getMessage());
        }
        
        // 转换结果为包含文件URL的响应
        PageResult<Content> pageResult = esResult.getData();
        List<Content> contentList = pageResult.getRecords();
        List<ContentWithFilesResponse> responseList = new ArrayList<>();
        
        // 为每个内容获取对应的文件URL
        for (Content content : contentList) {
            List<ContentFile> files = contentFileMapper.selectByContentId(content.getId());
            List<String> fileUrls = files.stream()
                    .map(ContentFile::getFileUrl)
                    .collect(Collectors.toList());
            
            // 使用工厂方法转换为ContentWithFilesResponse
            ContentWithFilesResponse response = ContentWithFilesResponse.fromContent(content, fileUrls);
            responseList.add(response);
        }
        
        PageResult<ContentWithFilesResponse> responsePageResult = new PageResult<>(
                pageResult.getTotal(), responseList);
        
        return Result.success(responsePageResult);
    }
    
    /**
     * 检查语义检索是否可用
     * @return 检查结果
     */
    @GetMapping("/semantic/status")
    public Result<Map<String, Object>> checkSemanticSearchStatus() {
        boolean available = chineseVectorService.isModelLoaded();
        Map<String, Object> result = new HashMap<>();
        result.put("available", available);
        result.put("message", available ? "语义检索功能可用" : "语义检索功能不可用，词向量模型未加载");
        
        return Result.success(result);
    }
    
    /**
     * 计算两段文本的语义相似度
     * @param text1 第一段文本
     * @param text2 第二段文本
     * @return 相似度结果
     */
    @GetMapping("/semantic/similarity")
    public Result<Map<String, Object>> calculateSimilarity(
            @RequestParam String text1,
            @RequestParam String text2) {
        
        if (!chineseVectorService.isModelLoaded()) {
            return Result.error("词向量模型未加载，无法计算相似度");
        }
        
        double similarity = chineseVectorService.calculateSimilarity(text1, text2);
        Map<String, Object> result = new HashMap<>();
        result.put("similarity", similarity);
        result.put("text1", text1);
        result.put("text2", text2);
        
        return Result.success(result);
    }
    
    /**
     * 重新加载词向量模型
     * @return 加载结果
     */
    @PostMapping("/semantic/reload-model")
    public Result<Map<String, Object>> reloadVectorModel() {
        try {
            chineseVectorService.initWordVectorModel();
            boolean loaded = chineseVectorService.isModelLoaded();
            
            Map<String, Object> result = new HashMap<>();
            result.put("loaded", loaded);
            result.put("message", loaded ? "词向量模型重新加载成功" : "词向量模型加载失败");
            
            if (loaded) {
                return Result.success(result);
            } else {
                return Result.error("词向量模型加载失败");
            }
        } catch (Exception e) {
            log.error("重新加载词向量模型失败", e);
            return Result.error("词向量模型加载失败：" + e.getMessage());
        }
    }
    
    /**
     * 清除语义搜索缓存
     * @return 清除结果
     */
    @PostMapping("/semantic/clear-cache")
    public Result<String> clearSemanticSearchCache() {
        try {
            // 实现实际的语义搜索缓存清理逻辑
            String pattern = SEMANTIC_CACHE_PREFIX + "*";
            Set<String> keys = redisTemplate.keys(pattern);
            if (keys != null && !keys.isEmpty()) {
                redisTemplate.delete(keys);
                log.info("已清除{}个语义搜索缓存", keys.size());
                return Result.success("已成功清除" + keys.size() + "个语义搜索缓存");
            }
            return Result.success("没有找到需要清除的语义搜索缓存");
        } catch (Exception e) {
            log.error("清除语义搜索缓存失败", e);
            return Result.error("清除缓存失败: " + e.getMessage());
        }
    }
    
    /**
     * 手动刷新ElasticSearch索引
     * @return 刷新结果
     */
    @PostMapping("/refresh-index")
    public Result<String> refreshIndex() {
        try {
            boolean success = elasticsearchService.refreshContentIndex();
            if (success) {
                return Result.success("索引刷新成功，所有内容现在都可以被搜索");
            } else {
                return Result.error("索引刷新失败，请查看服务器日志");
            }
        } catch (Exception e) {
            log.error("手动刷新索引时发生错误", e);
            return Result.error("刷新索引失败: " + e.getMessage());
        }
    }

    /**
     * 检查内容是否已被索引
     * @param contentId 内容ID
     * @return 检查结果
     */
    @GetMapping("/check-indexed/{contentId}")
    public Result<Map<String, Object>> checkContentIndexed(@PathVariable Long contentId) {
        try {
            boolean indexed = elasticsearchService.isContentIndexed(contentId);
            Map<String, Object> result = new HashMap<>();
            result.put("contentId", contentId);
            result.put("indexed", indexed);
            return Result.success(result);
        } catch (Exception e) {
            log.error("检查内容索引状态失败", e);
            return Result.error("检查索引状态失败: " + e.getMessage());
        }
    }

    /**
     * 重建全部索引 - 仅管理员可用
     * @param request HTTP请求
     * @return 重建结果
     */
    @PostMapping("/rebuild-index")
    public Result<String> rebuildIndex(HttpServletRequest request) {
        try {
            // 验证管理员权限
            String role = com.pro01.apirednote.utils.TokenUtil.getRole(request);
            if (!"admin".equals(role)) {
                return Result.error("只有管理员可以执行重建索引操作");
            }
            
            // 执行重建索引
            int count = elasticsearchService.rebuildContentIndex();
            return Result.success("索引重建成功，处理了" + count + "条内容");
        } catch (Exception e) {
            log.error("重建索引失败", e);
            return Result.error("重建索引失败: " + e.getMessage());
        }
    }
} 